package com.dup.jxlh.admin.service.auth.impl;

import com.alibaba.fastjson.JSONObject;
import com.dup.jxlh.admin.entities.auth.AuthInfo;
import com.dup.jxlh.admin.entities.auth.AuthPerm;
import com.dup.jxlh.admin.entities.auth.AuthUser;
import com.dup.jxlh.admin.mapper.AuthMapper;
import com.dup.jxlh.admin.service.auth.AuthRedisTokenStoreService;
import com.dup.jxlh.admin.service.auth.AuthService;
import com.dup.jxlh.api.enums.ResultEnum;
import com.dup.jxlh.api.exception.AppException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.security.oauth2.provider.OAuth2Request;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

@Service("authServiceImpl")
public class AuthServiceImpl implements AuthService {
    private final String PERM_PRE = "auth_perm_flag:";

    @Resource
    private AuthMapper authMapper;

    @Resource(name = "redisTemplateB")
    private RedisTemplate redisTemplate;

    @Autowired
    private AuthRedisTokenStoreService authRedisTokenStoreService;

    @Override
    public AuthInfo findByPhone(String phone) {
        return authMapper.findByPhone(phone);
    }

    @Override
    public ArrayList<AuthPerm> getAuthPerms(BigInteger roleId) {
        return authMapper.getAuthPerms(roleId);
    }

    @Override
    public void resetCurrentAuthUserPerm(Authentication auth) {
        String token = getCurrentAuthUserToken(auth);
        OAuth2Authentication oAuth2Authentication =
                authRedisTokenStoreService.readAuthentication(token);
        OAuth2Request oAuth2Request = oAuth2Authentication.getOAuth2Request();
        OAuth2Authentication oAuth2Auth
                = new OAuth2Authentication(oAuth2Request, resetCurrentAuthentication(auth));
        OAuth2AccessToken oAuth2AccessToken =
                authRedisTokenStoreService.readAccessToken(token);
        authRedisTokenStoreService.storeAccessToken(oAuth2AccessToken, oAuth2Auth);
        delCurrentAuthPermFlag(auth);
    }

    @Override
    public Boolean checkCurrentAuthPermFlag(String name) {
        if (name != null && redisTemplate.opsForValue().get(PERM_PRE + name) != null) {
            return true;
        }
        return false;
    }

    @Override
    public Boolean createCurrentAuthPermFlag(String name) {
        try {
            redisTemplate.opsForValue().set(PERM_PRE + name, 1);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    private Authentication resetCurrentAuthentication(Authentication auth) {
        AuthInfo admin = findByPhone(auth.getName());
        if (!admin.getEnable()) {
            throw new AppException(ResultEnum.USER_ENABLE_ERROR);
        }

        ArrayList<AuthPerm> permissionList = getAuthPerms(admin.getRoleId());

        Set<GrantedAuthority> updatedAuthorities = new HashSet<>();
        if (!permissionList.isEmpty()) {
            for (AuthPerm perm : permissionList) {
                updatedAuthorities.add(new SimpleGrantedAuthority(perm.getName()));
            }
        }

        AuthUser principal = (AuthUser) auth.getPrincipal();
        principal.setAuthorities(updatedAuthorities);
        principal.setDepartmentId(admin.getDepartmentId());
        principal.setLevel(admin.getLevel());
        principal.setRealname(admin.getRealname());

        Authentication newAuth = new UsernamePasswordAuthenticationToken(principal, auth.getCredentials(), updatedAuthorities);
        SecurityContextHolder.getContext().setAuthentication(newAuth);
        return newAuth;
    }

    private void delCurrentAuthPermFlag(Authentication auth) {
        redisTemplate.delete(PERM_PRE + auth.getName());
    }

    private String getCurrentAuthUserToken(Authentication auth) {
        JSONObject jsonObject = (JSONObject) JSONObject.toJSON(auth.getDetails());
        return jsonObject.getObject("tokenValue", String.class);
    }
}
